{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Single-Qubit Simulator\n",
    "\n",
    "\n",
    "*Copyright (c) 2021 Institute for Quantum Computing, Baidu Inc. All Rights Reserved.*"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Outline\n",
    "Quantum simulators can simulate the behavior of real quantum computers with high fidelity, helping users learn quantum computing knowledge, despite the difficulty of accessing quantum computing hardware due to shortage of resources. Additionally, quantum simulator can perform numerical simulations for researchers and developers to help them accelerate quantum computing algorithm development and quantum chip manufacturing. This tutorial describes how to use Quanlse's single-qubit simulator to consider the dephasing noise and amplitude noise and simulate the gate operation and readout process of superconducting single-qubit system. The outline of this tutorial is as follows:\n",
    "- Introduction\n",
    "- Preparation\n",
    "- Define simulator parameters and pulses\n",
    "- Running the simulator\n",
    "- Simulation Results\n",
    "- User case: Pi pulses calibration using Rabi oscillation\n",
    "- Summary"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Introduction\n",
    "\n",
    "  At the pulse level, a single-qubit's control and readout process requires applying pulses to the qubit. In this tutorial, the single-qubit simulator enables the simulation of the kinetic evolution of the control and readout process based on the control pulse, readout pulse, and noise parameters input by the user.\n",
    "  \n",
    "  The system Hamiltonian of a three-level superconducting qubit in the rotating frame after RWA (Rotating Wave Approximation) can be written as \\[1\\]:\n",
    "$$\n",
    "\\hat{H}_{\\rm sys}(t) = \\hat{H}_{\\rm anharm} + \\hat{H}_{\\rm noise}(t) + \\hat{H}_{\\rm ctrl}(t).\n",
    "$$\n",
    "\n",
    "where the anharmonicity term is related to qubit's anharmonicity parameter $\\alpha$:\n",
    "$$\n",
    "\\hat{H}_{\\rm anharm} = \\frac{\\alpha}{2} \\hat{a}^\\dagger \\hat{a}^\\dagger \\hat{a}\\hat{a}.\n",
    "$$\n",
    "\n",
    "Where $\\hat{a}^\\dagger$ and $\\hat{a}$ are the creation and annihilation operators of the three-level qubit, respectively. In this single-qubit simulator, we specifically introduce two noise terms: the dephasing noise $\\hat{H}_{\\rm deph}$ due to fluctuations of the ambient magnetic field and the amplitude noise $\\hat{H}_{\\rm amp}$ due to fluctuations of the control pulse waveform \\[1\\]:\n",
    "$$\n",
    "\\hat{H}_{\\rm noise}(t) = \\hat{H}_{\\rm deph} + \\hat{H}_{\\rm amp}(t),\n",
    "$$\n",
    "\n",
    "$$\n",
    "\\hat{H}_{\\rm deph} = \\frac{1}{2}\\mu \\hat{a}^\\dagger \\hat{a},\n",
    "$$\n",
    "\n",
    "$$\n",
    "\\hat{H}_{\\rm amp}(t) = \\epsilon \\hat{H}_{\\rm ctrl}.\n",
    "$$\n",
    "\n",
    "Here, the probability distributions of the dephase noise parameter $\\mu$ and the amplitude noise parameter $\\epsilon$ follow Gaussian and Lorentzian distributions, respectively:\n",
    "$$\n",
    "P(\\mu) = \\frac{1}{\\sigma\\sqrt{2\\pi}} e^{-\\mu^2 / 2\\sigma^2},\n",
    "$$\n",
    "\n",
    "$$\n",
    "P(\\epsilon) = \\frac{1}{\\gamma\\pi(1+\\epsilon^2)}.\n",
    "$$\n",
    "\n",
    "Therefore, we characterize these two types of noise by the coefficients $\\sigma$ and $\\gamma$ related to the probability distribution. Users can add the noise of the simulated system by entering these two parameters."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Preparation\n",
    "After you have successfully installed Quanlse, you could run the Quanlse program below following this tutorial. To run this particular tutorial, you would need to import the following packages from Quanlse and other commonly-used Python libraries:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Import 1-qubit noisy simulator at the pulse level\n",
    "from Quanlse.remoteSimulator import remoteNoisySimulator as noisySimulator\n",
    "from Quanlse.Utils.Plot import plotSched\n",
    "\n",
    "# Import the tool to design the pulse schedule\n",
    "from Quanlse.Utils.Waveforms import addPulse\n",
    "\n",
    "# Import other math tools\n",
    "from math import pi, exp\n",
    "\n",
    "# Import tools for visualization\n",
    "from matplotlib import pyplot as plt\n",
    "\n",
    "import numpy as np\n",
    "\n",
    "# Import tool for analysis\n",
    "from scipy.optimize import curve_fit"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "To use the Quanlse Cloud Service, we need to acquire a token from http://quantum-hub.baidu.com to get access to the cloud. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Import Define class and set the token for cloud service\n",
    "# Please visit http://quantum-hub.baidu.com\n",
    "from Quanlse import Define\n",
    "Define.hubToken = ''"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Define simulator parameters and pulses\n",
    "We first define the number of experimental executions `shots`, the detuning parameter $\\alpha$ for the qubit,  and the parameters $\\sigma$ and $\\gamma$ associated with noise (both default values are 0, i.e., the noiseless case).\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "shots = 512  # The number of shots \n",
    "anharm = -0.3472 * (2 * pi)  # The anharmonicity of the qubit, GHz\n",
    "deph_sigma = 0.01120487  # Dephasing error\n",
    "amp_gamma = 0.0159529  # Amplitude (over-rotation) error"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Then we use `addPulse` to define the pulse sequence. This function passes the strings `x` and `y` into the parameter `channel` to define the channel (X or Y channel) to which the pulse is applied; the parameters `t0` and `tg` are the start time and duration of the pulse waveform, respectively; and `f` represents the pulse waveform that the user wants to apply.\n",
    "\n",
    "The followings are three examples showing how we can define a pulse waveform in the simulator using `addPulse()`. Before we start, we need to create an empty list to initialize the pulse sequence:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "ctrlSeq = []  # Initialize the pusle sequence by creating an empty list "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "- **Using preset waveform functions:**\n",
    "Users can define the pulse by using the waveform preset by Quanlse and the input waveform parameters."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "tg0 = 20  # The duration of the pulse\n",
    "para0 = {'a': 0.2, 'tau': tg0 / 2, 'sigma': tg0 / 8}  # The parameters of the gaussian waveform\n",
    "ctrlSeq.append(addPulse(channel='x', t0=0, t=tg0, f='gaussian', para=para0))  # Add this pulse to the pulse sequence"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "- **Using user-defined waveform functions:**\n",
    "Users can also customize the waveform function in the form of `func(t, args)`, where the parameter `t` is the pulse duration."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "def func(t, args):\n",
    "    a, tau, sigma = args[\"a\"], args[\"tau\"], args[\"sigma\"]\n",
    "    if sigma == 0:\n",
    "        return 0\n",
    "    return a * exp(- ((t - tau) ** 2 / (2 * sigma ** 2)))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "and add it to the pulse sequence:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "tg1 = 30\n",
    "para1 = {'a': 0.2, 'tau': tg1 / 2, 'sigma': tg1 / 8}\n",
    "ctrlSeq.append(addPulse(channel='y', t0=tg0, t=tg1, f=func, para=para1))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "- **Using a user-defined waveform sequence:**\n",
    "Users can also enter a list describing the pulse amplitude directly into `seq`."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "tg2 = 40\n",
    "para2 = {'a': 0.3, 'tau': tg2 / 2, 'sigma': tg2 / 8}\n",
    "seq = [func(t, para2) for t in range(tg2)]\n",
    "ctrlSeq.append(addPulse(channel='x', t0=tg0+tg1, t=tg2, seq=seq))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "In addition, the readout process also requires us to define the readout pulse, using the function `addPulse()`, while the `channel` is correspondingly changed to `readout`. Here, users need to pay attention that the readout pulse must be added after the end of all control operations, i.e., the start time of the readout pulse `t0` should be bigger than the end time of the control pulse:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "roSeq = addPulse(channel='readout', t0=tg0+tg1+tg2, t=400, f='square', para={'a': 0.8})"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "We can use the function  `plotSched()` to visualize the user-defined pulse sequence. If the readout pulse is not passed, then the readout pulse is not displayed. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "plotSched(ctrlSeq)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "To display the readout pulse sequence, run the code in the cell below by using ``plotSched``："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "plotSched(waveDataCtrl=ctrlSeq, waveDataReadout=roSeq)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Running the simulator\n",
    "\n",
    "After the above preparations are done, we can pass the defined pulse sequence into the simulator `noisySimulator()` (if no readout pulse is passed, there is no readout simulation process). We first simulate without noise, i.e., without passing in the noise parameters $\\sigma$ and $\\gamma$, while printing to see the control pulse sequence："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "res0 = noisySimulator(waveDataCtrl=ctrlSeq, waveDataReadout=roSeq, shots=shots)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "It is also possible to pass in the previously defined noise parameters to run simulations with noise introduced:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "res1 = noisySimulator(dephSigma=deph_sigma, ampGamma=amp_gamma, waveDataCtrl=ctrlSeq, waveDataReadout=roSeq, shots=shots)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Result\n",
    "\n",
    "The function `noisySimulator` returns a dictionary of results with the following keys:\n",
    "- 'prob0': the probability that the result is '0' without the readout simulation.\n",
    "- 'prob1': the probability that the result is '1' without the readout simulation.\n",
    "- 'iq_data': the IQ data result of the readout simulation.\n",
    "- 'counts': the result of the counts after readout simulation.\n",
    "\n",
    "That is, we can view the readout results in different forms:\n",
    "\n",
    "- **0-1 count results:** \n",
    "We can obtain the population of the results both without and with readout simulations to view the effect of readout noise."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "label = ['0', '1']\n",
    "x = np.arange(len(label))\n",
    "# without noise term and readout simulation\n",
    "y1 = [res0['prob0'], res0['prob1']]\n",
    "# without noise term but with readout simulation\n",
    "y2 = [res0['counts']['0']/shots, res0['counts']['1']/shots]\n",
    "width = 0.35\n",
    "\n",
    "fig, ax = plt.subplots()\n",
    "\n",
    "plt.bar(x - width / 2, y1, width=width, color='blue', label='without readout simulation')\n",
    "\n",
    "plt.bar(x + width / 2, y2, width=width, color='red', label='with readout simulation')\n",
    "\n",
    "for i, v in enumerate(y1):\n",
    "    plt.text(x[i] - width / 2, v + 0.01, str(round(v, 3)))\n",
    "\n",
    "for i, v in enumerate(y2):\n",
    "    plt.text(x[i] + width / 2, v + 0.01, str(round(v, 3)))\n",
    "\n",
    "ax.set_ylabel('population')\n",
    "ax.set_title('0-1 counts without noise term')\n",
    "ax.set_xticks(x)\n",
    "ax.set_xticklabels(label)\n",
    "ax.legend()\n",
    "\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Similarly, it is possible to view the resulting population numbers for both noiseless and noisy simulations."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "label = ['0', '1']\n",
    "x = np.arange(len(label))\n",
    "# without noise term but with readout simulation\n",
    "y1 = [res0['counts']['0']/shots, res0['counts']['1']/shots]\n",
    "# with noise term and readout simulation\n",
    "y2 = [res1['counts']['0']/shots, res1['counts']['1']/shots]\n",
    "\n",
    "# plot the population results\n",
    "width = 0.35\n",
    "\n",
    "fig, ax = plt.subplots()\n",
    "\n",
    "plt.bar(x - width / 2, y1, width=width, color='blue', label='without noise term')\n",
    "\n",
    "plt.bar(x + width / 2, y2, width=width, color='red', label='with noise term')\n",
    "\n",
    "for i, v in enumerate(y1):\n",
    "    plt.text(x[i] - width / 2, v + 0.01, str(round(v, 3)))\n",
    "\n",
    "for i, v in enumerate(y2):\n",
    "    plt.text(x[i] + width / 2, v + 0.01, str(round(v, 3)))\n",
    "\n",
    "ax.set_ylabel('population')\n",
    "ax.set_title('0-1 counts with readout simulation')\n",
    "ax.set_xticks(x)\n",
    "ax.set_xticklabels(label)\n",
    "ax.legend()\n",
    "\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**IQ distribution data results:**\n",
    "\n",
    "When a readout pulse is passed to the parameter `waveDataReadout` of the function `noisySimulator()`, the data returned by the function already includes the distribution of the readout results in phase space. The user can obtain the IQ distribution by processing the key values corresponding to the key `iq_data` of the returned result dictionary \\[2\\]."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "plt.scatter(np.array(res1['iq_data']['0'])[:, 0], np.array(res1['iq_data']['0'])[:, 1], marker='.', label='0')\n",
    "plt.scatter(np.array(res1['iq_data']['1'])[:, 0], np.array(res1['iq_data']['1'])[:, 1], marker='.', label='1')\n",
    "plt.xlabel('$I$')\n",
    "plt.ylabel('$Q$')\n",
    "plt.legend()\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "In the above figure, each dot on the plane represents a certain readout result, and the number of dots is the number of executions `shots`. The larger the overlap in the distribution of the different colored dots, the worse the readout results.\n",
    "\n",
    "Below, we use an example further to understand the usage scenario of this single-qubit simulator."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## User case: Pi pulses calibration using Rabi oscillation\n",
    "\n",
    "In this section, we perform calibration experiments of Pi pulses using Rabi oscillations, i.e., fixing the pulse duration, varying the pulse amplitude, and obtaining the population number of the $|1\\rangle$ state. We use this single-qubit simulator to model this process.\n",
    "\n",
    "First, we define the range of pulse amplitudes `ampList` and the number of samples `num` for the experiment and the values of the other pulse parameters described in the previous section of the tutorial.\n",
    "\n",
    "Note: The simulation will take approximately 3-5 minutes. If you want to reduce the simulation's running time, you can reduce the number of runs `shots` and the number of samples `num`, but the simulation results will also be worse."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "shots = 512  # The number of shots \n",
    "anharm = -0.3472 * (2 * pi)  # The anharmonicity of the qubit, GHz\n",
    "deph_sigma = 0.01120487  # Dephasing error\n",
    "amp_gamma = 0.0159529  # Amplitude (over-rotation) error\n",
    "num = 40  # The number of sampling points\n",
    "ampList = np.linspace(0, 0.4, num)  # The list of the amplitudes \n",
    "tg_ro = 100  # The duration of the readout pulse\n",
    "tg_ctrl = 60  # The duration of the control pulse"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Then we use the `for` loop to simulate different pulse amplitudes and record the $|1\\rangle$ state population both without and with readout simulation for each different amplitude."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Add readout simulation channel \n",
    "waveDataReadout = addPulse(channel='readout', t0=tg_ctrl, t=tg_ro, f='square', para={'a': 0.8})\n",
    "\n",
    "prob1List = []\n",
    "counts1List = []\n",
    "count = 1\n",
    "\n",
    "for amp in ampList:\n",
    "    print(f'running data for amp: {amp}, count: {count} in {num}\\n')\n",
    "    count += 1\n",
    "    para = {'a': amp, 'tau': tg_ctrl / 2, 'sigma': tg_ctrl / 8}\n",
    "    waveData = addPulse(channel='x', t0=0, t=tg_ctrl, f='gaussian', para=para)\n",
    "    res = noisySimulator(waveDataCtrl=waveData, waveDataReadout=waveDataReadout, anharm=anharm, dephSigma=deph_sigma, ampGamma=amp_gamma, shots=shots)\n",
    "    prob1List.append(res['prob1'])\n",
    "    counts1List.append(res['counts']['1'])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "The results of Rabi oscillations with and without readout simulations are plotted separately: "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "y1 = prob1List\n",
    "y2 = (np.array(counts1List) / shots).tolist()\n",
    "\n",
    "plt.plot(ampList, prob1List, '.b', label='without readout simulation')\n",
    "plt.plot(ampList, np.array(counts1List) / shots, '.r', label='with readout simulation')\n",
    "plt.xlabel('Amplitude')\n",
    "plt.ylabel('Probability of being in |1>')\n",
    "plt.title('Rabi Oscillation')\n",
    "plt.legend(loc='upper right')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "When controlling a single qubit, the Pi pulse (that is, the $\\pi$ pulse) corresponds to the operation of rotating the qubit around the X-axis by an angle of 180 degrees on the Bloch sphere. Here, since our qubit's initial state is $|0\\rangle$ state by default, this operation can transform our qubit into $|1\\rangle$ state under ideal conditions. So we can use the Rabi oscillation experiment to find the pulse amplitude corresponding to such operation by getting the Rabi oscillation curve shown in the graph above, that is, the Pi pulse amplitude described in the tutorial. We can see that when the amplitude is 0, qubit is in the ground state $|0\\rangle$; when the amplitude is about 0.17, there is approximately $100\\%$ probability that the qubit is flipped into $|1\\rangle$ state. Thus we can know: in this single-qubit system we defined, the Pi pulse amplitude is about 0.17.\n",
    "\n",
    "Next, we fit the obtained curve to obtain a more accurate Pi pulse amplitude, here we choose to use the cosine function $y = -a\\cos{(bx)} + 0.49$ as the fitting function. For different hardware parameters and sampling intervals, users can also customize the corresponding fitting function."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# define the fitting curve\n",
    "def fit(x, a, b):\n",
    "    return -a * np.cos(b * x) + 0.49"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "We fit the above Rabi oscillogram using the `scipy.curve_fit` function to find the Pi pulse amplitude simulated by the simulator."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "paraFit, cov = curve_fit(fit, ampList, y2)\n",
    "step = 0.01\n",
    "y3 = [fit(x, paraFit[0], paraFit[1]) for x in np.arange(ampList[0], ampList[-1], step)]"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "After the fit is finished, we can draw a graph to see the fit:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "plt.plot(np.arange(ampList[0], ampList[-1], step), y3, label='fitting')\n",
    "plt.plot(ampList, y2, '.r', label='with readout simulation')\n",
    "plt.xlabel('Amplitude')\n",
    "plt.ylabel('Probability of being in |1>')\n",
    "plt.title('Rabi Oscillation')\n",
    "plt.legend(loc='upper right')\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "We see that the fit function effectively fits the simulation results with the readout process, and thus the calibrated Pi pulse amplitude can be obtained as follows:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "piAmp = 0 + step * y3.index(max(y3))\n",
    "print(f'Pi pulse amplitude: {piAmp}')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "In the experiment, we can use the data from the calibrated Pi pulses as a basis for other advanced superconducting quantum control experiments."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Summary\n",
    "\n",
    "This tutorial describes how to simulate a superconducting single-qubit measurement and readout process considering partial noise using a quantum pulse and visualize the results. Users can click on this link [tutorial-1qubit-simulator.ipynb](https://github.com/baidu/Quanlse/tree/master/Tutorial/EN/tutorial-1qubit-simulator.ipynb) to jump to the corresponding GitHub page for this Jupyter Notebook documentation to get the relevant code, try the different parameter values for further exploring the function of the Quanlse Simulator module."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Reference\n",
    "\n",
    "\\[1\\] [Carvalho, Andre RR, et al. \"Error-robust quantum logic optimization using a cloud quantum computer interface.\" *arXiv preprint arXiv:2010.08057* (2020).](https://arxiv.org/abs/2010.08057)\n",
    "\n",
    "\\[2\\] [Blais, Alexandre, et al. \"Circuit quantum electrodynamics.\" *arXiv preprint arXiv:2005.12667* (2020).](https://arxiv.org/abs/2005.12667)"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python [conda env:Quanlse]",
   "language": "python",
   "name": "quanlse"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.8.8"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}